import os

from swift.common import db_replicator
from swift.common.utils import list_from_csv, whataremyips

"""
This is a tool to explictly trigger replication for the assigned partitions lays on specified device to its peers.
One senario could be data lost found for particular path like: account/c/obj,
then use swift-get-node tools we could know all the nodes and partitions for account, c and obj.
We could check if those partitions are still on target positions, and if not recover should be triggered.
One option is after repair one replica, could use this tool explictly to kickoff one round replication,
the reconcile should be done for all the replicas asap.
"""

class Replicator(db_replicator.Replicator):
    def traverse_dir(self, dir_id, partitions):

        def walk_parts(data_dir, parts, node_id):
            for part in parts:
                part_dir = os.path.join(data_dir, str(part))
                if not os.path.isdir(part_dir):
                    self.logger.warning(_('Skipping part %s because path is not found') % part)
                    continue
                suffixes = os.listdir(part_dir)
                for suffix in suffixes:
                    suff_dir = os.path.join(part_dir, suffix)
                    if not os.path.isdir(suff_dir):
                        continue
                    hashes = os.listdir(suff_dir)
                    for hsh in hashes:
                        hash_dir = os.path.join(suff_dir, hsh)
                        if not os.path.isdir(hash_dir):
                            continue
                        object_file = os.path.join(hash_dir, hsh + '.db')
                        if os.path.exists(object_file):
                            yield (part, object_file, node_id)

        data_dir = dir_id[0]
        node_id = dir_id[1]
        yield walk_parts(data_dir, partitions, node_id).next()

    def run_once(self, *args, **kwargs):
        """Run a replication pass once."""
        device = kwargs.get("device")
        partitions = list_from_csv(kwargs.get("partitions"))
        if not device or not partitions:
            self.logger.error(_('invalid argument for replication, devices and partitions must be specified'))
            return

        self._zero_stats()
        dir_id = None
        ips = whataremyips()
        if not ips:
            self.logger.error(_('ERROR Failed to get my own IPs?'))
            return

        for node in self.ring.devs:
            if node and node['ip'] in ips and node['port'] == self.port:
                if self.mount_check and not os.path.ismount(
                        os.path.join(self.root, node['device'])):
                    self.logger.warn(
                        _('Skipping %(device)s as it is not mounted') % node)
                    continue

                #unlink_older_than(
                #    os.path.join(self.root, node['device'], 'tmp'),
                #    time.time() - self.reclaim_age)
                datadir = os.path.join(self.root, node['device'], self.datadir)
                if device == node['device'] and os.path.isdir(datadir):
                    dir_id = (datadir, node['id'])
                    break

        if not dir_id:
            self.logger.warning(_('Skipping device %s because not found/mounted') % device)

        self.logger.info(_('Beginning replication run'))
        for part, object_file, node_id in self.traverse_dir(dir_id, partitions):
            self.cpool.spawn_n(
                self._replicate_object, part, object_file, node_id)

        self.cpool.waitall()
        self.logger.info(_('Replication run OVER'))
        self._report_stats()